/*
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package output;

import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.PriorityQueue;
import java.util.regex.PatternSyntaxException;

import main.Main;

import org.apache.log4j.Logger;

/**
 * A class that holds a queue of the outgoing IRCOutputs, and moderates access to that queue
 * 
 * 
 */
public enum OutputQueue {
   INSTANCE;

   private final PriorityQueue<IRCOutput> queue;
   private boolean mainChanLock = false;
   private boolean adminChanLock = false;

   private OutputQueue() {
	  queue = new PriorityQueue<IRCOutput>(10);
   }

   /**
    * Adds an IRCOutput to the queue, given that it's not a duplicate of one that already exists in the queue
    * 
    * @param in
    */
   public synchronized void add(final IRCOutput in) {
	  if (in != null && !queue.contains(in)) {
		 queue.add(in);
		 notifyAll();
	  }
   }

   /**
    * Clears the queue of any outputs targetted at the specified users, or clears that queue completely for all users
    * 
    * @param user
    * @return
    */
   public synchronized boolean cancelOutput(final String user) {
	  if ("".equals(user))
		 return false;
	  if (user.equals("all")) {
		 Iterator<IRCOutput> it = queue.iterator();
		 while (it.hasNext()) {
			if (!"".equals(it.next().getSender())) {
			   it.remove();
			}
		 }
	  } else {
		 Iterator<IRCOutput> it = queue.iterator();
		 while (it.hasNext()) {
			if (user.equals(it.next().getSender())) {
			   it.remove();
			}
		 }
	  }
	  mainChanLock = false;
	  adminChanLock = false;
	  return true;
   }

   /**
    * Used by threads to report in and release any lock it had on a channel
    * 
    * @param target
    */
   public synchronized void finishedWork(final String target) {
	  if (target.equals(Main.getBotSettings().getMainChannel())) {
		 mainChanLock = false;
	  }
	  if (target.equals(Main.getBotSettings().getAdminChannel())) {
		 adminChanLock = false;
	  }
	  notifyAll();
   }

   /**
    * Returns the first available IRCOutput in the queue. Available referring to the fact that all output threads are not
    * necessarily the preferred choice for handling a specific output and also that if another thread is already writing to a
    * channel, no other thread should be able to disrupt that printout and mix up the outputs so they are hard to seperate from
    * each other
    * 
    * @param worker
    * @return
    */
   public synchronized IRCOutput next(final String worker) {
	  try {
		 if (!queue.isEmpty()) {
			Iterator<IRCOutput> it = queue.iterator();
			while (it.hasNext()) {
			   IRCOutput temp = it.next();
			   boolean found = false;
			   if (temp.getTarget().equals(Main.getBotSettings().getMainChannel())) {
				  if (!mainChanLock && ("".equals(temp.getPreferredHandler()) || worker.equals(temp.getPreferredHandler()))) {
					 mainChanLock = true;
					 found = true;
				  }
			   } else if (temp.getTarget().equals(Main.getBotSettings().getAdminChannel())) {
				  if (!adminChanLock && ("".equals(temp.getPreferredHandler()) || worker.equals(temp.getPreferredHandler()))) {
					 adminChanLock = true;
					 found = true;
				  }
			   } else if ("".equals(temp.getPreferredHandler()) || worker.equals(temp.getPreferredHandler())) {
				  found = true;
			   }
			   if (found && temp != null) {
				  it.remove();
				  notifyAll();
				  return temp;
			   }
			}
		 }
	  } catch (NoSuchElementException e) {
		 Logger.getLogger("Error").error("", e);
	  } catch (PatternSyntaxException e) {
		 Logger.getLogger("Error").error("", e);
	  }
	  notifyAll();
	  return null;
   }
}
